import asyncio
import logging
import math
from datetime import date

from lxautoplat.case import CrawlerPage

from crawler.service.automation_platform_request import global_automation_platform

LOGGER = logging.getLogger(__name__)


class BaseCrawlerPage(CrawlerPage):
    # 总条数选择器
    TOTAL_COUNT_SELECTOR = '.ant-pagination-total-text'
    # 总条数
    total_count = 0
    '''第一行id'''
    first_row_id = ''

    @staticmethod
    def _save_workbook(wb, file_path):
        """保存工作簿并记录日志"""
        try:
            wb.save(file_path)
            LOGGER.info("数据已保存到 %s", file_path)
        except Exception as e:
            LOGGER.error("保存文件 %s 时出错: %s", file_path, str(e))
            raise

    @staticmethod
    async def _click_element(element, element_name):
        """点击元素并记录日志"""
        try:
            await element.click()
            await asyncio.sleep(1)
            LOGGER.info("已打开 %s", element_name)
        except Exception as e:
            LOGGER.error("点击 %s 时出错: %s", element_name, str(e))
            raise

    @staticmethod
    async def _get_row_data(row, page_num, row_num, columns):
        """获取行数据并记录日志"""
        row_data = []
        cells = await row.query_selector_all('.ag-cell')
        for col_name, col_id in columns.items():
            try:
                value = await BaseCrawlerPage.get_content_index(cells, col_id)
                row_data.append(value)
            except Exception as e:
                LOGGER.error("获取第 %d 页第 %d 行 %s 列数据时出错: %s",
                             page_num, row_num, col_name, str(e))
        LOGGER.info("第 %d 页第 %d 行数据: %s", page_num, row_num, row_data)
        return row_data

    async def _get_element(self, selector, element_name):
        """获取元素并记录日志"""
        try:
            element = await self.page.query_selector(selector)
            if element:
                LOGGER.info("%s 元素存在", element_name)
            else:
                LOGGER.warning("%s 元素不存在", element_name)
            return element
        except Exception as e:
            LOGGER.error("获取 %s 元素时出错: %s", element_name, str(e))
            return None

    async def _expand_menu(self, menu_element, aria_expanded):
        """展开菜单并记录日志"""
        try:
            aria_expanded_value = await menu_element.get_attribute(aria_expanded)
            if aria_expanded_value == "false":
                LOGGER.info("菜单未展开，点击展开")
                await menu_element.click()
                await asyncio.sleep(1)
            else:
                LOGGER.info("菜单已展开")
        except Exception as e:
            LOGGER.error("展开菜单时出错: %s", str(e))
            raise

    async def _set_date_range(self, start_date, end_date):
        """设置日期范围并记录日志"""
        if start_date and end_date:
            LOGGER.info("给创建时间-日期周期框赋值")
            if isinstance(start_date, date):
                start_date = start_date.strftime("%Y-%m-%d")
            if isinstance(end_date, date):
                end_date = end_date.strftime("%Y-%m-%d")
            try:
                await self.set_ant_picker_range(start_date, end_date)
                LOGGER.info("已给创建时间-日期周期框赋值")
            except Exception as e:
                LOGGER.error("给创建时间-日期周期框赋值时出错: %s", str(e))
                raise

    async def _click_search_button(self, search_button_selector):
        """点击搜索按钮并记录日志"""
        try:
            await self.page.click(search_button_selector)
            LOGGER.info("已点击搜索按钮")
        except Exception as e:
            LOGGER.error("点击搜索按钮时出错: %s", str(e))
            raise

    async def _parse_table_data(self, ws, file_path, wb, columns):
        """解析表格数据并记录日志"""
        page_num = 1
        while True:
            page_data = []
            table_element = await self._get_element(self.table_selector, "表格")
            if not table_element:
                break
            # 等待表格元素重新加载
            await self.wait_data_loading(table_element)
            LOGGER.info("开始获取第 %d 页表格数据", page_num)
            try:
                await self.page.evaluate("document.body.style.zoom='50%'")
                rows = await table_element.query_selector_all('.ag-row')
                for row_num, row in enumerate(rows, start=1):
                    row_data = await self._get_row_data(row, page_num, row_num, columns)
                    page_data.append(row_data)

                # 一页读完 写入文件
                for row in page_data:
                    ws.append(row)
                self._save_workbook(wb, file_path)
                # 更新进度
                await self.update_progress(len(ws['A']))
                LOGGER.info("成功获取第 %d 页表格数据", page_num)
            except Exception as e:
                LOGGER.error("获取第 %d 页表格数据时出错: %s", page_num, str(e))
            finally:
                LOGGER.info("恢复浏览器")
                await self.page.evaluate("document.body.style.zoom='100%'")

            next_button = await self._get_element(self.next_button_selector, "下一页按钮")
            if not next_button or await next_button.is_disabled():
                # 没有下一页按钮或按钮不可用，退出循环
                LOGGER.info("没有下一页按钮或按钮不可用，结束表格数据获取")
                break
            # 点击下一页按钮
            await self._click_element(next_button, "下一页按钮")
            page_num += 1

    async def wait_data_loading(self, table_element):
        LOGGER.info("等待表格元素重新加载")
        await asyncio.sleep(1)
        await table_element.wait_for_selector('.ag-row', state='visible', strict=False)
        await self.page.wait_for_function(
            """
            (element) => {
                const role = element.getAttribute('role');
                return role === 'rowgroup';
            }
            """,
            arg=table_element,
            timeout=10000  # 超时时间，单位为毫秒，这里设置为 10 秒
        )

        # 最多进行 5 次尝试，检查表格第一行的 'comp-id' 属性是否发生变化，以此判断新数据是否加载完成
        for _ in range(5):
            # 获取表格中所有的行元素
            rows = await table_element.query_selector_all('.ag-row')
            # 若表格中存在行元素
            if rows:
                # 获取表格的第一行元素
                row = rows[0]
                # 获取第一行元素的 'comp-id' 属性值
                comp_id = await row.get_attribute('comp-id')
                # 若当前第一行的 'comp-id' 属性值与之前记录的不同，说明数据可能已更新
                if comp_id != self.first_row_id:
                    # 更新记录的第一行 'comp-id' 属性值
                    self.first_row_id = comp_id
                    # 跳出循环，结束检查
                    break
            # 若未检测到属性变化，等待 1 秒后再次尝试
            await asyncio.sleep(1)

        # 获取总条数
        if self.total_count == 0:
            total_count_element = await self.page.query_selector(self.TOTAL_COUNT_SELECTOR)
            if total_count_element:
                total_count_text = await total_count_element.inner_text()
                try:
                    self.total_count = int(total_count_text.split(' ')[1])
                    LOGGER.info("总条数: %d", self.total_count)
                except (IndexError, ValueError):
                    LOGGER.error("解析总条数时出错，文本内容: %s", total_count_text)
        LOGGER.info("表格元素重新加载完成")

    # 给日期周期框赋值
    async def set_ant_picker_range(self, start_date, end_date):
        try:
            # 定位日期范围选择器输入框，根据实际情况修改选择器
            picker_selector = '.ant-picker-range'
            await self.page.wait_for_selector(picker_selector)
            await self.page.click(picker_selector)

            # 先清空输入框
            await self.page.fill(self.create_time_start_selector, "")

            # 输入开始日期
            await self.page.type(self.create_time_start_selector, start_date)

            # 按 Tab 键切换到结束日期输入框
            await self.page.keyboard.press("Tab")

            # 输入结束日期
            await self.page.type(self.create_time_end_selector, end_date)

            # 按回车键确认选择
            await self.page.keyboard.press("Enter")

        except Exception as e:
            LOGGER.error("给日期周期框赋值出错: %s", str(e))
            raise

    # 获取cell内容
    @staticmethod
    async def get_content_index(cells, col_id):
        for cell in cells:
            try:
                # 获取 col-id 属性值
                aria_colindex = await cell.get_attribute('col-id')
                if aria_colindex == str(col_id):
                    return await cell.inner_text()
            except Exception as e:
                LOGGER.error("获取 cell 的 col-id 属性时出错: %s", str(e))
        return ''

    async def update_progress(self, current_count):
        """更新进度"""
        if self.total_count == 0:
            return
        try:
            progress = (current_count - 1) / self.total_count * 100
            progress = math.floor(progress)
            await global_automation_platform.process_run(progress)
        except Exception as e:
            LOGGER.error("更新进度时出错: %s", str(e))
